/*
  flamingo - 2D Game Engine
  Copyright (C) 2009 Bradley Zeis

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

  Bradley Zeis
  flamingoengine@gmail.com
*/

/* Mesh Initialization (old)
 * -------------------------
 *
 * 1) Create all of the MeshNodes and VMeshNodes when you load the raw mesh
 *    data from a file.
 *
 * 2) Instantiate a Mesh, passing the VMeshNodes and MeshNodes created earlier
 *      2a) Mesh_init loads Points from the MeshNodes into the
 *          vcoords and tcoords members of the Mesh. These members are Vertex
 *          Arrays that OpenGL uses to draw geometric data.
 *      2b) The Mesh also stores the MeshNodes in the vnodes and tnodes
 *          members of the Mesh to keep track of all of the MeshNodes.
 *
 * 3) Now that the Mesh is instantiated and the MeshNodes know where to update
 *    the data, you can assign the Mesh to a Sprite.
 *      3a) The outer VMeshNodes are found, and these vertices become the outside
 *          edge of the Sprite (which is used for collision detection). This
 *          will be implemented with a Polygon. The vertices of the Polygon will
 *          be references to the same MeshNodes that are stored in the Mesh.
 *          This means the Polygon will know how to update the Mesh when the
 *          outside edge is changed AND the Polygon will be updated when the
 *          MeshNodes change.
 *
 * 4) Now that everything is set up, updating the Mesh becomes simple. All you
 *    have to do is get a reference to one of the MeshNodes and change the
 *    x or y coordinate. When the MeshNode is changed, it updates the data
 *    in the Mesh's internal Vertex Array and the changes will be drawn to the
 *    screen.
 *
 */

#define FLMATH      1
#define MESH        1

#include "flamingo.h"
#include "mesh.h"
#include "flmath.h"

extern PyTypeObject __declspec(dllimport) Vector_Type;

PyTypeObject __declspec(dllexport) Bone_Type;
PyTypeObject __declspec(dllexport) Mesh_Type;
PyTypeObject __declspec(dllexport) MeshNode_Type;
PyTypeObject __declspec(dllexport) VMeshNode_Type;

/* -------- UTILITY -------- */
MeshNode *uNewMeshNode(double x, double y) {
    return (MeshNode *)PyObject_CallFunction((PyObject *)&MeshNode_Type, "((dd))", x, y);
}

VMeshNode *uNewVMeshNode(double x, double y) {
    return (VMeshNode *)PyObject_CallFunction((PyObject *)&VMeshNode_Type, "((dd))", x, y);
}

int uBone_resize_children(Bone *bone, int allocate) {
    Bone **new_children = PyMem_Resize(bone->children, Bone *, bone->allocated + allocate);
    if (new_children == NULL)
        return -1;
    bone->children = new_children;
    bone->allocated += allocate;
    return 0;
}

int uBone_add_child(Bone *parent, Bone *child) {
    if (parent->child_count == parent->allocated) {
        if (uBone_resize_children(parent, parent->child_count + 1) == -1)
            return -1;
    }
    Py_INCREF(child);
    parent->children[parent->child_count] = child;
    parent->child_count += 1;
    return 0;
}

/* -------- BONE METHODS -------- */
/* Initialization */
void Bone_dealloc(Bone *self) {
    Py_XDECREF(self->parent);
    int i;
    for (i = 0; i < self->child_count; i++) {
        Py_XDECREF(self->children[i]);
    }
    PyMem_Del(self->children);
    Py_XDECREF(self->v);
    self->ob_type->tp_free((PyObject *)self);
}

PyObject *Bone_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
    Bone *self;
    self = (Bone *)type->tp_alloc(type, 0);

     if (self != NULL) {
        self->parent = NULL;
        self->child_count = 0;
        self->allocated = 0;
        self->children = NULL;
        self->v = NULL;
        self->min_length = 0;
        self->max_length = -1;
        self->min_angle = 0;
        self->max_angle = 360;
    }

    return (PyObject *)self;
}

int Bone_init(Bone *self, PyObject *args, PyObject *kwds) {
    // It is assumed that the data passed is valid, there is no
    // error checking. Users should not initialize Bones.
    
    if (!PyArg_ParseTuple(args, "OO", &self->parent, &self->v))
        return -1;
    
    if ((PyObject *)self->parent == Py_None) {
        Py_DECREF(Py_None);
        self->parent = NULL;
    }
    
    if (self->parent != NULL) {
        uBone_add_child(self->parent, self);
    }
    
    Py_INCREF(self->v);
    
    self->children = PyMem_New(Bone *, 1);
    self->allocated = 1;

    if (self->children == NULL) {
        PyErr_SetNone(PyExc_MemoryError); 
        return -1;
    }
    
    return 0;
}

/* Presentation */
PyObject *Bone_repr(Bone *self) {
    char string[64];
    sprintf(string, "<mesh.Bone(%i)>", (int)self);
    return PyString_FromString(string);
}

PyObject *Bone_str(Bone *self) {
    return Bone_repr(self);
}

/* Sequence Operations */
Py_ssize_t Bone_len(Bone *self) {
    return self->child_count;
}

PyObject *Bone_item(Bone *self, Py_ssize_t i) {
    if (i > self->child_count - 1) {
        PyErr_SetString(PyExc_IndexError, "Bone index out of range");
        return NULL;
    }
    
    if (i < 0)
        i = self->child_count + i;
    
    Py_INCREF(self->children[i]);
    return (PyObject *)self->children[i];
    Py_RETURN_NONE;
}


/* General Function */
PyObject *Bone_children(Bone *self) {
    PyObject *children = PyTuple_New(self->child_count);
    int i;
    for (i = 0; i < self->child_count; i++) {
        Py_INCREF(self->children[i]);
        PyTuple_SET_ITEM(children, i, (PyObject *)self->children[i]);
    }
    return children;
}

PyObject *Bone_add_child(Bone *self, PyObject *args) {
    Bone *new_child;
    if (!PyArg_ParseTuple(args, "O", &new_child))
        return NULL;
    
    if (!PyObject_TypeCheck(new_child, &Bone_Type)) {
        PyErr_Format(PyExc_TypeError, "expected 'flamingo.mesh.Bone', not '%s'",
                     new_child->ob_type->tp_name);
        return NULL;
    }
    
    uBone_add_child(self, new_child);
    Py_RETURN_NONE;
}

/* Properties */
PyObject *Bone_getlength(Bone *self, void *closure) {
    return Vector_getlength((Vector *)self->v, closure);
}

int Bone_setlength(Bone *self, PyObject *value, void *closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "Bone length must be numerical");
        return -1;
    }
    double val;
    if (!(val = PyFloat_AsDouble(value)))
        return -1;
    
    if (self->flags & BONE_CONSTRAIN_LENGTH) {
        if (val < self->min_length)
            val = self->min_length;
            
        if (val > self->max_length && self->max_length > 0)
            val = self->max_length;
    }
    
    if (val <= 0)
        return 0;

    PyObject *nval = Py_BuildValue("d", val);
    return Vector_setlength((Vector *)self->v, nval, closure);
    Py_DECREF(nval);
    return 0;
}

PyObject *Bone_getangle(Bone *self, void *closure) {
    return Vector_getangle((Vector *)self->v, closure);
}

int Bone_setangle(Bone *self, PyObject *value, void *closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "Bone angle must be numerical");
        return -1;
    }
    double val;
    if (!(val = PyFloat_AsDouble(value)))
        return -1;
    
    if (self->flags & BONE_CONSTRAIN_ANGLE) {
        if (val < self->min_angle)
            val = self->min_angle;
            
        if (val > self->max_angle)
            val = self->max_angle;
    }

    PyObject *nval = Py_BuildValue("d", val);
    return Vector_setangle((Vector *)self->v, nval, closure);
    Py_DECREF(nval);
    return 0;
}

/* Method Declaration */
PyMemberDef Bone_members[] = {
    {"parent", T_OBJECT_EX, offsetof(Bone, parent), READONLY, NULL},
    {"min_length", T_DOUBLE, offsetof(Bone, min_length), 0, NULL},
    {"max_length", T_DOUBLE, offsetof(Bone, max_length), 0, NULL},
    {"min_angle", T_DOUBLE, offsetof(Bone, min_angle), 0, NULL},
    {"max_angle", T_DOUBLE, offsetof(Bone, max_angle), 0, NULL},
    {NULL}
};

PySequenceMethods Bone_SequenceMethods = {
    (lenfunc)Bone_len,                          /* sq_length */
    0,                                          /* sq_concat */
    0,                                          /* sq_repeat */
    (ssizeargfunc)Bone_item,                    /* sq_item */
    0,
    0,                                          /* sq_ass_item */
    0,                                          /* sq_contains */
    0,                                          /* sq_inplace_concat */
    0,                                          /* sq_inplace_repeat */
};

PyMethodDef Bone_methods[] = {
    {"children", (PyCFunction)Bone_children, METH_NOARGS, NULL},
    {"add_child", (PyCFunction)Bone_add_child, METH_VARARGS, NULL},
    {NULL}
};

PyGetSetDef Bone_getsets[] = {
    {"length", (getter)Bone_getlength, (setter)Bone_setlength, NULL, NULL},
    {"angle", (getter)Bone_getangle, (setter)Bone_setangle, NULL, NULL},
    {NULL}
};

/* -------- MESH METHODS -------- */
/* Initialization */
void Mesh_dealloc(Mesh *self) {
    PyMem_Del(self->vcoords);
    PyMem_Del(self->tcoords);

    int i;
    for (i = 0; i < self->length; i++) {
        Py_XDECREF(self->vnodes[i]);
        Py_XDECREF(self->tnodes[i]);
    }

    PyMem_Del(self->vnodes);
    PyMem_Del(self->tnodes);
    self->ob_type->tp_free((PyObject *)self);
}

PyObject *Mesh_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
    Mesh *self;
    self = (Mesh *)type->tp_alloc(type, 0);

     if (self != NULL) {
        self->length = 0;
        self->vcoords = NULL;
        self->tcoords = NULL;
        self->vnodes = NULL;
        self->tnodes = NULL;
        self->vlist = NULL;
        self->tlist = NULL;
    }

    return (PyObject *)self;
}

int Mesh_init(Mesh *self, PyObject *args, PyObject *kwds) {
    // It is assumed that the data passed is valid, there is no
    // error checking. Users should not initialize Meshes.

    PyObject *vvert_list, *tvert_list;
    if (!PyArg_ParseTuple(args, "OO", &vvert_list, &tvert_list))
        return -1;

    /* Initialize Structure */
    int length = PySequence_Length(vvert_list);
    self->length = length;

    self->vcoords = PyMem_New(Point, length);
    if (self->vcoords == NULL)
        return -1;

    self->tcoords = PyMem_New(Point, length);
    if (self->tcoords == NULL)
        return -1;

    self->vnodes = (Vector **)PyMem_New(VMeshNode *, length);
    if (self->vnodes == NULL)
        return -1;

    self->tnodes = (Vector **)PyMem_New(MeshNode *, length);
    if (self->tnodes == NULL)
        return -1;
    
    self->vlist = PyList_New(length);
    self->tlist = PyList_New(length);

    /* Copy over data from args - creates new references */
    Vector *current;
    int i;
    for (i = 0; i < length; i++) {
        current = (Vector *)GETITEM(vvert_list, i);
        self->vnodes[i] = current;  // New Reference
        self->vcoords[i] = (Point)*(current->pt);
        self->vnodes[i]->pt = (Point *)&self->vcoords[i];
        Py_INCREF(self->vnodes[i]);
        PyList_SET_ITEM(self->vlist, i, (PyObject *)self->vnodes[i]);

        current = (Vector *)GETITEM(tvert_list, i);
        self->tnodes[i] = current;  // New Reference
        self->tcoords[i] = (Point)*(current->pt);
        self->tnodes[i]->pt = (Point *)&self->tcoords[i];
        Py_INCREF(self->tnodes[i]);
        PyList_SET_ITEM(self->tlist, i, (PyObject *)self->tnodes[i]);
    }

    return 0;
}

PyObject *Mesh_hash(Mesh *self) {
    return PyInt_FromLong(PyObject_Hash(PyInt_FromLong((int)self)));
}

/* Presentation */
PyObject *Mesh_repr(Mesh *self) {
    char string[64];
    sprintf(string, "<mesh.Mesh(%i)>", (int)self);
    return PyString_FromString(string);
}

PyObject *Mesh_str(Mesh *self) {
    return Mesh_repr(self);
}

/* Sequence Methods */
Py_ssize_t Mesh_len(Mesh *self) {
    return self->length;
}

/* Properties */
PyObject *Mesh_getv(Mesh *self, void *closure) {
    Py_INCREF(self->vlist);
    return self->vlist;
}

PyObject *Mesh_gett(Mesh *self, void *closure) {
    Py_INCREF(self->tlist);
    return self->tlist;
}

/* Method Declaration */
PyMemberDef Mesh_members[] = {
    {NULL}
};

PySequenceMethods Mesh_SequenceMethods = {
    (lenfunc)Mesh_len,                           /* sq_length */
    0,                                           /* sq_concat */
    0,                                           /* sq_repeat */
    0,                                          /* sq_item */
    0,
    0,                                          /* sq_ass_item */
    0,                                          /* sq_contains */
    0,                                          /* sq_inplace_concat */
    0,                                          /* sq_inplace_repeat */
};

PyMethodDef Mesh_methods[] = {
    {"__hash__", (PyCFunction)Mesh_hash, METH_NOARGS, NULL},
    {NULL}
};

PyGetSetDef Mesh_getsets[] = {
    {"v", (getter)Mesh_getv, NULL, NULL, NULL},
    {"t", (getter)Mesh_gett, NULL, NULL, NULL},
    {NULL}
};

/* -------- MESHNODE METHODS -------- */
/* Initialization */
void MeshNode_dealloc(MeshNode *self) {
    PyMem_Del(self->pt);
    self->ob_type->tp_free((PyObject *)self);
}

PyObject *MeshNode_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
    MeshNode *self;
    self = (MeshNode *)type->tp_alloc(type, 0);

    if (self != NULL) {
        self->pt = PyMem_New(Point, 1);
        self->pt->x = 0;
        self->pt->y = 0;
    }

    return (PyObject *)self;
}

int MeshNode_init(MeshNode *self, PyObject *args) {
    return Vector_init((Vector *)self, args);
}

int VMeshNode_init(VMeshNode *self, PyObject *args) {
    // MeshNodes are meant for the Texture Coordinates, VMeshNodes
    // are meant for Vertex Coordinates. Once there is a skeletal
    // system in place, VMeshNodes will be able to change dynamically
    // as the skeleton moves.

    int i = Vector_init((Vector *)self, args);
    if (i == -1)
        return i;

    // Do some more initialization once there's a skeletal system
    return 0;
}

PyObject *MeshNode_hash(MeshNode *self) {
    return PyInt_FromLong(PyObject_Hash(PyInt_FromLong((int)self)));
}

/* Presentation */
PyObject *MeshNode_repr(MeshNode *self) {
    char string[64];
    sprintf(string, "<mesh.MeshNode([%f, %f])>", self->pt->x, self->pt->y);
    return PyString_FromString(string);
}

PyObject *MeshNode_str(MeshNode *self) {
    return MeshNode_repr(self);
}

PyObject *VMeshNode_repr(VMeshNode *self) {
    char string[64];
    sprintf(string, "<mesh.VMeshNode([%f, %f])>", self->pt->x, self->pt->y);
    return PyString_FromString(string);
}

PyObject *VMeshNode_str(VMeshNode *self) {
    return VMeshNode_repr(self);
}

/* General Functions */
PyObject *MeshNode_copy(MeshNode *self) {
    return PyObject_CallFunction((PyObject *)&MeshNode_Type, "((dd))", self->pt->x, self->pt->y);
}

PyObject *VMeshNode_copy(MeshNode *self) {
    return PyObject_CallFunction((PyObject *)&VMeshNode_Type, "((dd))", self->pt->x, self->pt->y);
}

/* Method Declaration */
PyMemberDef MeshNode_members[] = {
    {NULL}
};

PyMethodDef MeshNode_methods[] = {
    {"__hash__", (PyCFunction)MeshNode_hash, METH_NOARGS, NULL},
    {"copy", (PyCFunction)MeshNode_copy, METH_NOARGS, NULL},
    {NULL}
};

PyMethodDef VMeshNode_methods[] = {
    {"copy", (PyCFunction)VMeshNode_copy, METH_NOARGS, NULL},
    {NULL}
};

PyGetSetDef MeshNode_getsets[] = {
    {NULL}
};

/* -------- TYPE DECLARATIONS -------- */
PyTypeObject Bone_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
    "flamingo.mesh.Bone",                       /* tp_name */
    sizeof(Bone),                               /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor)Bone_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    (reprfunc)Bone_repr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &Bone_SequenceMethods,                      /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)Bone_str,                        /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
    0,                                          /* tp_doc */
    0,                                      /* tp_traverse */
    0,                                      /* tp_clear */
    0,                                      /* tp_richcompare */
    0,                                      /* tp_weaklistoffset */
    0,                                      /* tp_iter */
    0,                                      /* tp_iternext */
    Bone_methods,                               /* tp_methods */
    Bone_members,                               /* tp_members */
    Bone_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)Bone_init,                        /* tp_init */
    0,                                          /* tp_alloc */
    Bone_new,                                   /* tp_new */
};

PyTypeObject Mesh_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
    "flamingo.mesh.Mesh",                       /* tp_name */
    sizeof(Mesh),                               /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor)Mesh_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    (reprfunc)Mesh_repr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &Mesh_SequenceMethods,                      /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)Mesh_str,                        /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
    0,                                          /* tp_doc */
    0,                                      /* tp_traverse */
    0,                                      /* tp_clear */
    0,                                      /* tp_richcompare */
    0,                                      /* tp_weaklistoffset */
    0,                                      /* tp_iter */
    0,                                      /* tp_iternext */
    Mesh_methods,                               /* tp_methods */
    Mesh_members,                               /* tp_members */
    Mesh_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)Mesh_init,                        /* tp_init */
    0,                                          /* tp_alloc */
    Mesh_new,                                   /* tp_new */
};

PyTypeObject MeshNode_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
    "flamingo.mesh.MeshNode",                   /* tp_name */
    sizeof(MeshNode),                           /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor)MeshNode_dealloc,               /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    (reprfunc)MeshNode_repr,                    /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)MeshNode_str,                     /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
    0,                                          /* tp_doc */
    0,                                      /* tp_traverse */
    0,                                      /* tp_clear */
    0,                                      /* tp_richcompare */
    0,                                      /* tp_weaklistoffset */
    0,                                      /* tp_iter */
    0,                                      /* tp_iternext */
    MeshNode_methods,                           /* tp_methods */
    MeshNode_members,                           /* tp_members */
    MeshNode_getsets,                           /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)MeshNode_init,                    /* tp_init */
    0,                                          /* tp_alloc */
    MeshNode_new,                               /* tp_new */
};

PyTypeObject VMeshNode_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
    "flamingo.mesh.VMeshNode",                  /* tp_name */
    sizeof(VMeshNode),                          /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor)MeshNode_dealloc,               /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    (reprfunc)VMeshNode_repr,                   /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)VMeshNode_str,                    /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
    0,                                          /* tp_doc */
    0,                                      /* tp_traverse */
    0,                                      /* tp_clear */
    0,                                      /* tp_richcompare */
    0,                                      /* tp_weaklistoffset */
    0,                                      /* tp_iter */
    0,                                      /* tp_iternext */
    VMeshNode_methods,                          /* tp_methods */
    MeshNode_members,                           /* tp_members */
    MeshNode_getsets,                           /* tp_getset */
    &MeshNode_Type,                             /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)VMeshNode_init,                   /* tp_init */
    0,                                          /* tp_alloc */
    MeshNode_new,                               /* tp_new */
};

/* -------- MODULE INIT -------- */
PyMODINIT_FUNC
initmesh(void)
{
    PyObject *m;

    /* Initialize module */
    MeshNode_Type.tp_base = &Vector_Type;
    
    if (PyType_Ready(&Bone_Type) < 0)
        return;
    if (PyType_Ready(&Mesh_Type) < 0)
        return;
    if (PyType_Ready(&MeshNode_Type) < 0)
        return;
    if (PyType_Ready(&VMeshNode_Type) < 0)
        return;

    m = Py_InitModule("mesh", NULL);
    if (m == NULL)
        return;
    
    Py_INCREF(&Mesh_Type);
    PyModule_AddObject(m, "Bone", (PyObject *)&Bone_Type);
    Py_INCREF(&Mesh_Type);
    PyModule_AddObject(m, "Mesh", (PyObject *)&Mesh_Type);
    Py_INCREF(&MeshNode_Type);
    PyModule_AddObject(m, "MeshNode", (PyObject *)&MeshNode_Type);
    Py_INCREF(&VMeshNode_Type);
    PyModule_AddObject(m, "VMeshNode", (PyObject *)&VMeshNode_Type);
}
